import {
  getAlbumInfo,
  getAlbumTracks,
  getArtistInfo,
  getDiscography,
  getPlaylistInfo,
  getPlaylistTracks,
  getTrackInfo,
} from 'd-fi-core';
import spotifyUri from 'spotify-uri';
import * as spotify from '../api/spotify';
import * as tidal from '../api/tidal';
import {isrc2deezer, upc2deezer} from '../api/deezer';
import pMap from 'p-map';
import signale from './signale';
import type {albumType, trackType} from 'd-fi-core/dist/types';

type linkType = 'track' | 'album' | 'artist' | 'playlist';

type urlPartsType = {
  type:
    | 'track'
    | 'album'
    | 'audiobook'
    | 'artist'
    | 'playlist'
    | 'spotify-track'
    | 'spotify-album'
    | 'spotify-playlist'
    | 'spotify-artist'
    | 'tidal-track'
    | 'tidal-album'
    | 'tidal-playlist'
    | 'tidal-artist';

  id: string;
};

const getUrlParts = async (url: string): Promise<urlPartsType | null> => {
  if (url.startsWith('spotify:')) {
    const spotify = url.split(':');
    url = 'https://open.spotify.com/' + spotify[1] + '/' + spotify[2];
  }

  try {
    const {hostname} = new URL(url);
    const site = hostname.match(/deezer|spotify|tidal/);
    if (!site) {
      throw new Error('Unknown URL: ' + url);
    }

    switch (site[0]) {
      case 'deezer':
        const deezerUrlParts = url.split(/\/(\w+)\/(\d+)/);
        return {type: deezerUrlParts[1] as any, id: deezerUrlParts[2]};
      case 'spotify':
        const spotifyUrlParts = spotifyUri.parse(url);
        await spotify.setSpotifyAnonymousToken();
        return {type: ('spotify-' + spotifyUrlParts.type) as any, id: (spotifyUrlParts as any).id};
      case 'tidal':
        const tidalUrlParts = url.split(/\/(\w+)\/(\d+|\w+-\w+-\w+-\w+-\w+)/);
        return {type: ('tidal-' + tidalUrlParts[1]) as any, id: tidalUrlParts[2]};
      default:
        return null;
    }
  } catch {
    return null;
  }
};

const parseInfo = async (url: string) => {
  let info = await getUrlParts(url);
  if (!info) {
    return null;
  } else if (!info.id) {
    throw new Error('Unable to parse id');
  }

  let linktype: linkType = 'track';
  let linkinfo: trackType | albumType | {} = {};
  let tracks: trackType[] = [];

  switch (info.type) {
    case 'track':
      tracks.push(await getTrackInfo(info.id));
      break;

    case 'album':
    case 'audiobook':
      linkinfo = await getAlbumInfo(info.id);
      linktype = 'album';
      const albumTracks = await getAlbumTracks(info.id);
      tracks = albumTracks.data;
      break;

    case 'playlist':
      linkinfo = await getPlaylistInfo(info.id);
      linktype = 'playlist';
      const playlistTracks = await getPlaylistTracks(info.id);
      tracks = playlistTracks.data;
      break;

    case 'artist':
      console.log(signale.info('Fetching artist tracks. Please hold on.'));
      linkinfo = await getArtistInfo(info.id);
      linktype = 'artist';
      const artistAlbums = await getDiscography(info.id);
      await pMap(
        artistAlbums.data,
        async (album) => {
          const albumTracks = await getAlbumTracks(album.ALB_ID);
          tracks = [...tracks, ...albumTracks.data];
        },
        {concurrency: 10},
      );
      break;

    case 'spotify-track':
      const spotifyTrack = await spotify.spotifyApi.getTrack(info.id);
      tracks.push(await isrc2deezer(spotifyTrack.body.name, spotifyTrack.body.external_ids.isrc));
      break;

    case 'spotify-album':
      const spotifyAlbum = await spotify.spotifyApi.getAlbum(info.id);
      const [spotifyAlbumInfo, spotifyTracks] = await upc2deezer(
        spotifyAlbum.body.name,
        spotifyAlbum.body.external_ids.upc,
      );
      tracks = spotifyTracks;
      linkinfo = spotifyAlbumInfo;
      linktype = 'album';
      break;

    case 'spotify-playlist':
      const [spotifyPlaylistInfo, spotifyPlaylistTracks] = await spotify.spotifyPlaylist2Deezer(info.id);
      tracks = spotifyPlaylistTracks;
      linkinfo = spotifyPlaylistInfo;
      linktype = 'playlist';
      break;

    case 'spotify-artist':
      tracks = await spotify.spotifyArtist2Deezer(info.id);
      linktype = 'artist';
      break;

    case 'tidal-track':
      const tidalTrack = await tidal.getTrack(info.id);
      tracks.push(await isrc2deezer(tidalTrack.title, tidalTrack.isrc));
      break;

    case 'tidal-album':
      const tidalAlbum = await tidal.getAlbum(info.id);
      const [tidalAlbumInfo, tidalAlbumTracks] = await upc2deezer(tidalAlbum.title, tidalAlbum.upc);
      tracks = tidalAlbumTracks;
      linkinfo = tidalAlbumInfo;
      linktype = 'album';
      break;

    case 'tidal-playlist':
      const [tidalPlaylistInfo, tidalPlaylistTracks] = await tidal.playlist2Deezer(info.id);
      tracks = tidalPlaylistTracks;
      linkinfo = tidalPlaylistInfo;
      linktype = 'playlist';
      break;

    case 'tidal-artist':
      tracks = await tidal.artist2Deezer(info.id);
      linktype = 'artist';
      break;

    default:
      throw new Error('Unknown type');
  }

  return {info, linktype, linkinfo, tracks};
};

export default parseInfo;
